package no.sachmuld.methodcombination.api;

import java.util.ArrayList;
import java.util.List;

public class Condition {

    private List<Clause> clauses = new ArrayList<>();

    protected Condition() {
    }

    public Condition(boolean value) {
        clauses.add(new Clause(null, value));
    }

    public Condition(String description, boolean value) {
        clauses.add(new Clause(description, value));
    }

    public <T> Condition(String description, T expected, T actual) {
        clauses.add(new Clause(description, expected, actual));
    }

    public Condition and(String description, boolean value) {
        clauses.add(new Clause(description, value));
        return this;
    }

    public <T> Condition and(String description, T expected, T actual) {
        clauses.add(new Clause(description, expected, actual));
        return this;
    }

    public boolean getValue() {
        for (Clause c : clauses) {
            if (!c.getValue()) {
                return false;
            }
        }
        return true;
    }

    public List<Clause> getFailures() {
        if (!getValue()) {
            List<Clause> failures = new ArrayList<>();
            for (Clause clause : clauses) {
                if (!clause.getValue()) {
                    failures.add(clause);
                }
            }
            return failures;
        } else {
            return null;
        }
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("{");
        for (int i = 0; i < clauses.size(); i++) {
            if (i > 0) {
                sb.append(", ");
            }
            sb.append(clauses.get(i).toString());
        }
        sb.append("}");
        return sb.toString();
    }

    public static class Clause {
        private String description;
        private boolean value;
        private Object expected;
        private Object actual;
        private boolean hasExpectedAndActual;


        public Clause(String description, boolean value) {
            this.description = description;
            this.value = value;
            this.hasExpectedAndActual = false;
        }

        public Clause(String description, Object expected, Object actual) {
            this.description = description;
            this.value = expected == null ? actual == null : expected.equals(actual);
            this.expected = expected;
            this.actual = actual;
            this.hasExpectedAndActual = true;
        }

        public String getDescription() {
            return description;
        }

        public boolean getValue() {
            return value;
        }

        public Object getExpected() {
            return expected;
        }

        public Object getActual() {
            return actual;
        }

        public boolean hasExpectedAndActual() {
            return hasExpectedAndActual;
        }

        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();
            if (description != null) {
                sb.append(description);
                sb.append(": ");
            }
            sb.append(value);
            if (!value && hasExpectedAndActual) {
                sb.append(" (expected ");
                sb.append(expected);
                sb.append(" was ");
                sb.append(actual);
                sb.append(")");
            }
            return sb.toString();
        }
    }
}
